<?php
/**
 *
 * Copyright (C) Ross Singer 2009
 *
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

require_once 'Interface.php';
 
class brightonJangle implements DriverInterface {

    private $config;
    private $targets;
    private $client;
    
    function __construct() {
    
        $this->config = parse_ini_file('conf/brightonJangle.ini', true);
        $this->client = new BrightonJangleClient();
       
    }
    
    /**
     * Public Function which retrieves renew, hold and cancel features from the driver ini file.
     *
     * @param   string                  The name of the feature to be checked                   
     * 
     * @return  array                   An array with key-value pairs. Mandary key of "enabled".
     */

    public function getConfig($function)
    {

        if(isset($this->config[$function]) && $this->config[$function]['enabled'] == true) {
            $functionConfig = $this->config[$function];
        }
        else {
            $functionConfig = false;    
        }
    
        return $functionConfig;
    }
    
    private function db($username, $password, $url) {
    
        $user_agent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)";
	    $db = curl_init();
	    curl_setopt($db, CURLOPT_USERAGENT, $user_agent);
	    curl_setopt($db, CURLOPT_USERPWD, "$username:$password");
	    curl_setopt($db, CURLOPT_RETURNTRANSFER, 1);
	    curl_setopt($db, CURLOPT_URL, $url);
	    return curl_exec($db);
    
    }
    
     function is_hidden($statusCode) {
        
        $hideCodes = explode(":", $this->config['Catalog']['hideCodes']);              
        return in_array($statusCode, $hideCodes);
         
    }
    
    function is_holdForbidden($statusCode) {
        
        $holdForbiddenCodes = explode(":", $this->config['Catalog']['holdForbiddenCodes']); 
        return in_array($statusCode, $holdForbiddenCodes);
         
    }
    
    function getStatus($id) {
        
        // getItems expects ids in an array
        $jangleIds = array();
        $jangleIds[0] = $id;
        
        $items_array = $this->getStatuses($jangleIds);
        
        return $items_array;         
         
    }
     
    function getStatuses($jangleIds) {

        $number = 1;
        $items = $this->client->getItems($jangleIds);
        foreach(array_keys($items) as $resource) {
            $items_array = array(); 
            foreach($items[$resource] as $item) {
                
                $statusDetails = $item['content']->children($this->config['Catalog']['alto_namespace']);
                
                $alto_item = $statusDetails->TalisAlto->Item;
                
                if($alto_item) {
                
                $itemAtt = $alto_item->attributes();
                $id = (string)$itemAtt->id;
                $barcode = (string)$alto_item->barcode;
                $location = (string)$alto_item->activeSite->Location->name;
                $available = ((string)$alto_item->availability == "true")?true:false;
                $status = ((string)$alto_item->availabilityMessage != "")?(string)$alto_item->availabilityMessage:"Unknown";
                $callnumber = (string)$alto_item->Classification->classnumber;
                $duedate = ((string)$alto_item->dateAvailable !="")?(string)$alto_item->dateAvailable:"";
                $loan_type = (string)$alto_item->itemType;
                $statAtt = $alto_item->status->attributes();
                $itemStatus = trim((string)$statAtt->code);

                $hide = (empty($barcode) || $this->is_hidden($itemStatus));
                
                if(!$hide) {
                
                    $items_array[] = array('id'=>$resource,
                                       'availability'=>$available,
                                       'status'=>$status,
                                       'location'=>$location,
                                       'reserve' => 'No',
                                       'callnumber' => $callnumber,
                                       'duedate' =>  date("Y-M-d", strtotime((string)$duedate)),                            
                                       'number'=>$number++);
                }
                
                }
                    
            }
            
            $statuses[] = $items_array;
        }
        return $statuses;         
         
    }
     
    function getHolding($id) {
    
	//$id = 44672;
	$items = $this->client->getItems(array($id));
        $holding = array();
        foreach($items as $resource) {
            $number = 1; 
            foreach($resource as $item) {
                $holdingsDetails = $item['content']->children($this->config['Catalog']['alto_namespace']);              

                $alto_item = $holdingsDetails->TalisAlto->Item;
                
                if($alto_item) {
                
                $itemAtt = $alto_item->attributes();
                $bib_id = (string)$itemAtt->work;
                $item_id = (string)$itemAtt->id;
                $barcode = (string)$alto_item->barcode;
                // little hack, set location to be combination of location of loan type
                // so we have a subheading per location/loan-type combination
                // helps to work nicely with Sussex Uni - which doesn't have locations.
                $location = (string)$alto_item->activeSite->Location->name . " " . (string)$alto_item->itemType;
                $available = ((string)$alto_item->availability == "true")?true:false;
                $callnumber = (string)$alto_item->Classification->classnumber;
                $callnumber_suffix = (string)$alto_item->classificationSuffix;
                $duedate = ((string)$alto_item->dateAvailable !="")?(string)$alto_item->dateAvailable:"";
                $loan_type = (string)$alto_item->itemType;
                $statAtt = $alto_item->status->attributes();
                $itemStatus = trim((string)$statAtt->code);
                $itemStatusMsg = trim((string)$alto_item->status);
                $format = trim((string)$alto_item->format);
                $status = ((string)$alto_item->availabilityMessage != "")?(string)$alto_item->availabilityMessage:($itemStatusMsg != "")?$itemStatusMsg:"Unknown";
                $note = "";
                $hide="0";
                //$note = "$itemStatusMsg and $status";
                
                $hold_availability = !$this->is_holdForbidden($itemStatus);
                $hide = (empty($barcode) || $this->is_hidden($itemStatus));
               
                if ($itemStatusMsg != "Loanable") {
                    $note = "See main catalogue ($itemStatusMsg)";
                    $available = false;
                }
                   
                if($format == "PE") { $barcode = "Periodical"; }
                // if there is no due date then make sure it is available
                if (!$duedate) {
                    $available = true;
                }
                // if there is a classmark suffix add it to clasmark
                if ($callnumber_suffix) { $callnumber = $callnumber . "/" . $callnumber_suffix; }
                if ($itemStatusMsg == "Withdrawn") { $hide = "1";$available = false; }
                if ($available == false) { $status = "Unavailable"; }
                
                
                    if(!$hide) {
                                
                        $holding[] = array('id'=>$bib_id,
                                   'item_id'=>$item_id,
                                   'availability'=>$available,
                                   'status'=> ucfirst($status),
                                   'location'=> $location,
                                   'reserve' => 'No',
                                   'callnumber' => $callnumber,
                                   'duedate' =>  !empty($duedate)?date("Y-M-d", strtotime((string)$duedate)):"",   
                                   'number'=>$number++,
                                   'barcode' => $barcode,
                                   'hold_availability' => $hold_availability,
                                   'format_id' => $format,
                                   'format_display' => $format, 
                                   'loan_type' => $loan_type,
                                   'note' => $note);
                    }
                }                     
            }
        }
            
        return $holding;
         
    }
    
    function getPurchaseHistory($id) {
        return array();
    }
     
    public function patronLogin($barcode, $password) {
    
        $actor = $this->client->authenticate($barcode, $password);
        
        if(count($actor) > 0) {

            foreach($actor as $profile) {
         
                $userDetails = $profile['content']->children($this->config['Catalog']['alto_namespace']);
                $borrowerAttr = $userDetails->TalisAlto->Borrower->attributes();
                
                $id = (string)$borrowerAttr->id;
                $firstname = (string)$userDetails->TalisAlto->Borrower->firstNames;
                $lastname = (string)$userDetails->TalisAlto->Borrower->surname;
                $line1 = (string)$userDetails->TalisAlto->Borrower->primaryAddress->Address->line1;
                $line2 = (string)$userDetails->TalisAlto->Borrower->primaryAddress->Address->line2;
                $phone = (string)$userDetails->TalisAlto->Borrower->primaryAddress->Address->phone;
                $zip = (string)$userDetails->TalisAlto->Borrower->primaryAddress->Address->postcode;
                
                foreach ($userDetails->TalisAlto->Borrower->Contact as $contact) {
                    $found_email = false;
                    $found_term = false;
                    foreach($contact->attributes() as $a => $b) {
                        
                        if($a == "type" && $b == "email") {
                            $found_email = true;
                        }
                        if($a == "preferred" && $b == "T") {
                            $found_term = true;
                        }    
                    }
                    if($found_term == true && $found_email == true) {
                        $email = (string)$contact;
                    } 
                }
            }
            
            if($id != "") {
            
                return array(
                    'id' => $id,
                    'firstname' => $firstname,
                    'lastname' => $lastname,
                    'cat_username' => $barcode,
                    'cat_password' => $password,
                    'email' => $email,
                    'major' => null,
                    'college' => null);
            
            }
            else {
                return null;
            }              
        } 
        else {
            return null;
        }      
    }
    
    public function getMyTransactions($patron) {

        $items = $this->client->getActor($patron['id'], 'items', 'loan', 'loan_format');
   
        $transList = array();

        if(count($items) > 0) {

            foreach($items as $item) {
                $itemDetails = $item['content']->children($this->config['Catalog']['alto_namespace']);
                $itemAttr = $itemDetails->TalisAlto->Item->attributes();
                $itemId = (string)$itemAttr->id;
                $bibId = (string)$itemAttr->work;
                
                $transList[$bibId]['id'] = $bibId;
                $transList[$bibId]['ils_details']['item_id'] = $itemId;
                 $transList[$bibId]['ils_details']['renewable'] = true;
                
                $avail = $itemDetails->TalisAlto->Item->dateAvailable;
                if(!empty($avail)) {
                    $transList[$bibId]['ils_details']['duedate'] = date("Y-M-d", strtotime((string)$avail));
                }
            
            }    
        }

        return $transList;
            
    }
    
    public function getMyFines($patron) {
    
        $items = $this->client->getActor($patron['id'], 'items', false, 'fine_format');
        
        $fineList = array();

        if(count($items) > 0) {

            foreach($items as $item) {
                
                $itemDetails = $item['content']->children($this->config['Catalog']['alto_namespace']);
                              
                $itemAttr = $itemDetails->TalisAlto->Item->attributes();
                $itemId = (string)$itemAttr->id;
                $bibId = (string)$itemAttr->work;
                             
                $fines = $itemAttr = $itemDetails->TalisAlto->Item->fines;
                    
                if($fines) {
                    
                    $fine_amount = (string)$fines->loans;
                                  
                    $fineList[] = array('amount' => $fine_amount*100,
                                       'fine' => 'Over Due Loan Fee',
                                       'balance' => $fine_amount*100,
                                       'checkout' => 'Unknown',
                                       'duedate' => 'Unknown',
                                       'id' => $bibId,
                                       'item_id' => $itemId);
                }
            }    
        }
        
        return $fineList;
    }
    
     public function getMyHolds($patron) {

        $holds = $this->client->getActor($patron['id'], 'items', 'hold', 'hold_format');
   
        $holdList = array();

        if(count($holds) > 0) {

            foreach($holds as $hold) {
                $itemDetails = $hold['content']->children($this->config['Catalog']['alto_namespace']);
                $itemAttr = $itemDetails->TalisAlto->Item->attributes();
                $itemId = (string)$itemAttr->id;
                $bibId = (string)$itemAttr->work;
                
                $holdList[$bibId]['id'] = $bibId;
                $holdList[$bibId]['ils_details']['item_id'] = $itemId;
                $holdList[$bibId]['ils_details']['location']= (string)$itemDetails->TalisAlto->Item->Reservation->collectionSite->Location->name;
                $holdList[$bibId]['ils_details']['expiredate']= date("Y-M-d", strtotime((string)$itemDetails->TalisAlto->Item->Reservation->lastUsefulDateTime));
                $holdList[$bibId]['ils_details']['createdate']= date("Y-M-d", strtotime((string)$itemDetails->TalisAlto->Item->Reservation->effectiveDateTime));
                
                $resAttr = $itemDetails->TalisAlto->Item->Reservation->attributes();
                
                $holdList[$bibId]['ils_details']['recall_id']= (string)$resAttr->id;
                $holdList[$bibId]['ils_details']['position']= (string)$itemDetails->TalisAlto->Item->reservationQueueLength;

            }    
        }
        return $holdList;
    }
     
    public function getMyProfile($patron) {
                
        $actor = $this->client->getActor($patron['id'], false, false);
        
        if($actor && count($actor) > 0) {

            foreach($actor as $profile) {
         
                $profileDetails = $profile['content']->children($this->config['Catalog']['alto_namespace']);

                $firstname = (string)$profileDetails->TalisAlto->Borrower->firstNames;
                $lastname = (string)$profileDetails->TalisAlto->Borrower->surname;
                $line1 = (string)$profileDetails->TalisAlto->Borrower->primaryAddress->Address->line1;
                $line2 = (string)$profileDetails->TalisAlto->Borrower->primaryAddress->Address->line2;
                $phone = (string)$profileDetails->TalisAlto->Borrower->primaryAddress->Address->phone;
                $zip = (string)$profileDetails->TalisAlto->Borrower->primaryAddress->Address->postcode;
            }
            
            $patron = array('firstname' => $firstname,
                            'lastname' => $lastname,
                            'address1' => $line1,
                            'address2' => $line2,
                            'zip' => $zip,
                            'phone' => $phone,
                            'group' => "");
            return $patron;
        }
        
        else {
        
        return null;
        
        }
        
    }
    
    function getNewItems()
    {
        return array();
    }
    
    function getFunds()
    {
        return array();
    }
    
    function getDepartments()
    {
        return array();
    }

    function getInstructors()
    {
        return array();
    }

    function getCourses()
    {
        return array();
    }

    function findReserves($course, $inst, $dept)
    {
        return array();
    }
    
    function getSuppressedRecords()
    {
        return array();
    }
    
        public function getHoldLink($link)
    {

        $url = $this->config['Holds']['opacurl'];
        $HoldUrl = $url.'?sid=Talis:prod_talis&pid=key:'.$link['link']['sys_id'].';ArtifactType:Marc21Slim;searchLocation;talislms;';
        return $HoldUrl;
    }
    
    public function renewMyItemsLink($link)
    {

        $url = $this->config['Renewals']['opacurl'];
        $rewnewItemsUrl = $url;
        return $rewnewItemsUrl;
    }
    
    public function getCancelHoldLink($link)
    {

        $url = $this->config['cancelHolds']['opacurl'];
        $cancelHoldsUrl = $url;
        return $cancelHoldsUrl;
    }
    
    public function getPickUpLocations($patron) {

        if(isset($this->config['pickUpLibraries'])) {
            
            foreach($this->config['pickUpLibraries'] as $code => $library) {
        
                $pickresponse[] = array('libid' => $code,
                                        'libname' => $library);
            }
        }

        return $pickresponse;
    }
    
    public function getSysDetails($barcode)
    {
    
        $data = $this->client->searchJangle("actors", "jangle.username", $barcode);     

        if($data && count($data) > 0) {

            foreach($data as $sysDetails) {
         
                $userDetails = $sysDetails['content']->children($this->config['Catalog']['alto_namespace']);
                $borrowerAttr = $userDetails->TalisAlto->Borrower->attributes();
                
                $id = (string)$borrowerAttr->id;
                $firstname = (string)$userDetails->TalisAlto->Borrower->firstNames;
                $lastname = (string)$userDetails->TalisAlto->Borrower->surname;
                $line1 = (string)$userDetails->TalisAlto->Borrower->primaryAddress->Address->line1;
                $line2 = (string)$userDetails->TalisAlto->Borrower->primaryAddress->Address->line2;
                $phone = (string)$userDetails->TalisAlto->Borrower->primaryAddress->Address->phone;
                $zip = (string)$userDetails->TalisAlto->Borrower->primaryAddress->Address->postcode;
                
                
            }
            
            return array('id' => $id,
                         'firstname' => $firstname,
                         'lastname' => $lastname,
                         'cat_username' => $barcode,
                         'cat_password' => null,
                         'email' => null,
                         'major' => null,
                         'college' => null);
        }
            
        else {
            return null;
        }
        
    }
}
 
class BrightonJangleClient {

    private $config;
    
    function __construct() {
    
        // Get Config
        $config = parse_ini_file('conf/BrightonJangleConfig.ini', true);
        
        // Set Core Values
        $this->username = $config['core']['username'];
        $this->password = $config['core']['password'];
        $this->base_uri = $config['core']['base_uri'];
        $this->record_prefix = $config['core']['record_prefix'];
        $this->resource_format = $config['core']['resource_format'];
        $this->item_format = $config['core']['item_format'];
        $this->actor_format = $config['core']['actor_format'];
        $this->format_argument = $config['core']['format_argument'];
        $this->offset_argument = $config['core']['offset_argument'];
        $this->resource_category = $config['core']['resource_category'];
        
        // Load Custom Values
        if(!empty($config['actor'])) {
            $this->actorConfig = $config['actor'];
        }
        
    }

    function getItems($ids)
    {  
        $item_format = !empty($this->itemConfig['item_format']) ? $this->itemConfig['item_format'] : $this->item_format;
        
        $items = array();
        // Join the Resource identifiers and GET their Item relationships.
        $url = $this->base_uri.'/resources/'.join($ids,',').'/items/?'.$this->format_argument.'='.$item_format;
	//echo $url;

        $req = &new HTTP_Request($url);
        $req->setMethod(HTTP_REQUEST_METHOD_GET);
        $req->sendRequest();
        $code = $req->getResponseCode();
        if($code == '200') {
            $xml = simplexml_load_string($req->getResponseBody());
            if(count($xml->entry)!= 0) {
 
                foreach($xml->entry as $entry) {
                    $entry->registerXPathNamespace('atom','http://www.w3.org/2005/Atom');
                    $via = $entry->xpath('atom:link[@rel="via"]');
                        if(count($via)) {
                            $uri = parse_url((string)$via[0]['href']);
                            $id = array_pop(explode('/', $uri['path']));
                        } elseif (count($ids) == 1) {
                            $id = $ids[0];
                        }   else {
                          // There's no way to relate Items to Resources!
                        }
                    $format_link = $entry->xpath('atom:link[@jangle:format]');
                    $format = $format_link[0]->attributes('http://jangle.org/vocab/');
                    $content = $entry->content[0];
                    if(!$items[$id]) {
                        $items[$id] = array();
                    }
                    $items[$id][] = array("id"=>(string)$entry->id, "format"=>(string)$format, "content"=>$content);
                }
            }
        }
        return $items;
    }
     
    function getActor($id, $level = false, $function = false, $useFunctionFormat = false) {
        

         
        $url = $this->base_uri.'/actors/'.$id;
        
        if($level) {
            $url .= "/".$level;
        }
        if($function && $level) {
            $url .= "/-/".$function;
        }
        
        if($useFunctionFormat && !empty($this->actorConfig[$useFunctionFormat])) {
            $url .= "?".$this->format_argument.'='.$this->actorConfig[$useFunctionFormat];
        }
        else {
            $url .= "?".$this->format_argument.'='.$this->actor_format;
        }

        $req = $this->db($this->username, $this->password, $url);    

        if($req) {
            $xml = simplexml_load_string($req);
            if(count($xml->entry) == 0) {
                return false;
            }
            foreach($xml->entry as $entry) {

                $entry->registerXPathNamespace('atom','http://www.w3.org/2005/Atom');
                $format_link = $entry->xpath('atom:link[@jangle:format]');
                $format = $format_link[0]->attributes('http://jangle.org/vocab/');
                $content = $entry->content;
                $items[] = array("id"=>(string)$entry->id, "format"=>(string)$format, "content"=>$content);
            }
        }    
        return $items;
    }
    
    function searchJangle($level, $field, $query) {
    
        $url = $this->base_uri.'/'.$level.'/search?query='.$field.'='.$query;
        
        $req = $this->db($this->username, $this->password, $url);    

        if($req) {
            $xml = simplexml_load_string($req);
            if(count($xml->entry) == 0) {
                return false;
            }
            foreach($xml->entry as $entry) {

                $entry->registerXPathNamespace('atom','http://www.w3.org/2005/Atom');
                $format_link = $entry->xpath('atom:link[@jangle:format]');
                $format = $format_link[0]->attributes('http://jangle.org/vocab/');
                $content = $entry->content;
                $result[] = array("id"=>(string)$entry->id, "format"=>(string)$format, "content"=>$content);
            }
        }
        return $result;
         
    }
    
    function authenticate($barcode, $password) {
    
        $url = $this->base_uri.'/actors/';
        $req = $this->db($barcode, $password, $url);
        
        if($req) {
            $xml = simplexml_load_string($req);
            if(count($xml->entry) == 0) {
                return false;
            }
            foreach($xml->entry as $entry) {

                $entry->registerXPathNamespace('atom','http://www.w3.org/2005/Atom');
                $format_link = $entry->xpath('atom:link[@jangle:format]');
                $format = $format_link[0]->attributes('http://jangle.org/vocab/');
                $content = $entry->content;
                $actor[] = array("id"=>(string)$entry->id, "format"=>(string)$format, "content"=>$content);
            }
        }    
        return $actor;
        
    }
      
    private function db($username, $password, $url) {
    
        $user_agent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)";
	    $db = curl_init();
	    curl_setopt($db, CURLOPT_USERAGENT, $user_agent);
	    curl_setopt($db, CURLOPT_USERPWD, "$username:$password");
	    curl_setopt($db, CURLOPT_RETURNTRANSFER, 1);
	    curl_setopt($db, CURLOPT_URL, $url);
	    $req = curl_exec($db);
	    $info = curl_getinfo($db);
	    if(!curl_errno($db) && $info['http_code'] == "200") {
	        return $req;
        }
        else {
            return false;
        }
    }
}

?>
